﻿using System;
using System.Collections.Generic;

namespace RiskCalc.Modelo
{
    public static class Calculos
    {
        public static double mediaAritmetica(List<double> vetValores)
        {
            int tamanho = vetValores.Count;

            double soma = 0;

            foreach (double dado in vetValores)
            {
                soma += dado;
            }

            return soma / tamanho;
        }

        public static double covariancia(List<double> vetRetornos1, double media1, List<double> vetRetornos2, double media2)
        {
            double soma = 0;

            for (int i = 0; i < vetRetornos1.Count; i++)
            {
                soma += (vetRetornos1[i] - media1) * (vetRetornos2[i] - media2);
            }

            return soma / vetRetornos1.Count;
        }

        public static double desvioPadrao(double variancia)
        {
            return Math.Sqrt(variancia);
        }

        public static double variancia(double media, List<double> vetValores)
        {
            double sum = 0;

            foreach (double valor in vetValores)
            {
                sum += (valor - media) * (valor - media);
            }

            return sum / vetValores.Count;
        }
        
        public static double[,] multiplicarMatrizes(int numLinhasM1, int numColunasM1, int numColunasM2, double[,] m1, double[,] m2)  
        {
            double[,] result = new double[numLinhasM1, numColunasM2];

            for (int i = 0; i < numLinhasM1; i++)
            {
                for (int j = 0; j < numColunasM2; j++)
                {
                    result[i, j] = 0;

                    for (int k = 0; k < numColunasM1; k++)
                    {
                        result[i, j] += m1[i, k] * m2[k, j];
                    }
                }
            }

            return result;
        }

        public static Double correlacao(double covariancia, double desvioPadrao1, double desvioPadrao2)
        {
            return covariancia / (desvioPadrao1 * desvioPadrao2);
        }

        public static double[,] decomposicaoCholesky(double[,] matrizCovariancia)
        {
            throw new System.NotImplementedException();
        }

        public static double inversaDistNormal(Int32 confianca)
        {
            return 1.64485362695147;
        }

        public static double percentil(List<double> vetValores, int percentil)
        {
            // Ordena
            vetValores.Sort();

            double posicao = (vetValores.Count + 1) * (100 - percentil) / 100.0;
            double numeroEsquerda = 0.0d;
            double numeroDireita = 0.0d;

            double n = percentil / 100.0d * (vetValores.Count - 1) + 1.0d;

            if (posicao >= 1)
            {
                numeroEsquerda = vetValores[(int)System.Math.Floor(n) - 1];
                numeroDireita = vetValores[(int)System.Math.Floor(n)];
            }
            else
            {
                numeroEsquerda = vetValores[0];
                numeroDireita = vetValores[1];
            }

            if (numeroEsquerda == numeroDireita)
            {
                return numeroEsquerda;
            }
            else
            {
                double parte = n - System.Math.Floor(n);
                return numeroEsquerda + parte * (numeroDireita - numeroEsquerda);
            }
        }

        public static double perdaOuGanho(double posicao, double retorno)
        {
            return posicao * retorno;
        }

        public static double retornoGeometrico(double valorDiaAnterior, double valorDiaAtual)
        {
            return Math.Log(valorDiaAtual / valorDiaAnterior);
        }

        public static double retornoSimulado(double retornoOriginal)
        {
            return Math.Exp(retornoOriginal) - 1;
        }

        public static double[,] transpor(int numLinhas, int numColunas, double[,] matriz)
        {
            double[,] result = new double[numColunas, numLinhas];

            for (int i = 0; i < numLinhas; i++)
            {
                for (int j = 0; j < numColunas; j++)
                {
                    result[i, j] = matriz[j, i];
                }
            }

            return result;
        }

        public static double varParametrico(double posicao, double desvioPadrao, double media, double confianca)
        {
            // Equação do VaR pelo retorno geométrico
            return -1 * (posicao - (posicao *

              // "e" elevado a ()
              Math.Exp(

              // Integração da função densidade de probabilidade normal com média 0 e desvio padrão 1
              confianca * -1 *

               // Media dos Retornos Geométricos dos últimos <janelaDias> dias
              desvioPadrao +

              // Desvio Padrão dos Retornos Geométricos dos últimos cdias
              media)));
        }

        public static double varParametricoCarteira(double[,] vetVaRs, double[,] matrizCorrelacao)
        {
            double[,] result = new double[matrizCorrelacao.Length, matrizCorrelacao.Length];

            int numVars = matrizCorrelacao.Length;

            result = multiplicarMatrizes(1, numVars, numVars,

                     multiplicarMatrizes(1, numVars, numVars, vetVaRs, matrizCorrelacao),

                        transpor(matrizCorrelacao.Length, 1, vetVaRs));

            return Math.Sqrt(result[0, 0]);
        }
    }
}